package org.budo.support.fastjson;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.mvcs.Mvcs;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
public class Fastjson {
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * @see #toJSONStringSimply(TjMessage)
     */
    public static String toJSONString(Object val) {
        if (null == val) {
            return null;
        }

        try {
            return JSON.toJSONStringWithDateFormat(val, DATE_FORMAT, SerializerFeature.DisableCircularReferenceDetect);
        } catch (Throwable e) {
            log.error("#18 type=" + val.getClass().getName() + ", e=" + e);
            return JSON.toJSONStringWithDateFormat(val, DATE_FORMAT);
        }
    }

    public static <T> T parseObject(String text, Class<T> type) {
        if (null == text) {
            return null;
        }

        if ((!text.trim().startsWith("{") || !text.trim().endsWith("}")) //
                && (!text.trim().startsWith("[") || !text.trim().endsWith("]"))) {
            log.error("#50 parseObject error, text=" + text + ", type=" + type);
            return null;
        }

        return JSON.parseObject(text, type);
    }

    public static <T> T tryParseObject(String text, Class<T> type) {
        try {
            return parseObject(text, type);
        } catch (Exception e) {
            log.error("#62 parseObject error, text=" + text + ", type=" + type, e);
            return null;
        }
    }

    public static JSONObject parseObject(String text) {
        if (null == text || text.trim().isEmpty()) {
            log.warn("#71 parseObject, text=" + text + ", handler=" + Mvcs.getHandler() + ", uri=" + Mvcs.getRequestURI());

            if (log.isDebugEnabled()) {
                log.debug("#74 parseObject, text=" + text, new Throwable("#56 parseObject, text=" + text));
            }

            return null;
        }

        if (!text.trim().startsWith("{") || !text.trim().endsWith("}")) {
            log.error("#82 parseObject error, text=" + text + ", handler=" + Mvcs.getHandler() + ", uri=" + Mvcs.getRequestURI());
            return null;
        }

        try {
            return JSON.parseObject(text);
        } catch (Throwable e) {
            log.error("#59 parseObject error, text=" + text + ", e=" + e);

            throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
        }
    }

    public static Object parse(String text) {
        if (null == text || text.trim().isEmpty()) {
            log.error("#73 parse error, text=" + text);
            return null;
        }

        return JSON.parse(text);
    }

    public static JSONArray parseArray(String text) {
        if (null == text || text.trim().isEmpty()) {
            log.error("#82 parseArray error, text=" + text);
            return null;
        }

        return JSON.parseArray(text);
    }

    public static <T> List<T> parseArray(String text, Class<T> type) {
        if (null == text) {
            return null;
        }

        if (!text.trim().startsWith("[") || !text.trim().endsWith("]")) {
            log.error("#116 parseArray error, text=" + text + ", type=" + type);
            return null;
        }

        return JSON.parseArray(text, type);
    }

    @SuppressWarnings("unchecked")
    public static List<Map<String, Object>> toMapList(Object[] array) {
        return (List<Map<String, Object>>) JSON.toJSON(array);
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> toMap(Object val) {
        if (null == val) {
            return null;
        }

        return (Map<String, Object>) JSON.toJSON(val);
    }

    /**
     * 后面的会覆盖前面的
     */
    public static Map<String, Object> mergeToMap(Object obj1, Object obj2) {
        Map<String, Object> map = new HashMap<String, Object>();

        if (null != obj1) {
            Map<String, Object> map1 = toMap(obj1);
            map.putAll(map1);
        }

        if (null != obj2) {
            Map<String, Object> map2 = toMap(obj2);
            map.putAll(map2);
        }

        return map;
    }

    /**
     * 概略化 json 省略长属性
     */
    public static String summary(String json) {
        if (StringUtil.isBlank(json)) {
            return null;
        }

        if (json.length() < 512) {
            return json;
        }

        // 不是 json object
        if (!json.startsWith("{")) {
            return json;
        }

        // 只处理了 json object 未处理 json array
        Map<String, Object> message = JSON.parseObject(json);
        message = filterTooLongParam_2(message);

        return JSON.toJSONString(message);
    }

    private static Map<String, Object> filterTooLongParam_2(Map<String, Object> message) {
        if (null == message || message.isEmpty()) {
            return message;
        }

        for (Entry<String, Object> entry : message.entrySet()) {
            if (null == entry.getValue() || !(entry.getValue() instanceof String)) {
                continue;
            }

            // 只处理了 第一级 string 属性; 其他类型应不需处理; 更下级别或 array 类型 未处理
            String str = entry.getValue().toString();
            int len = str.length();
            if (len < 512) {
                continue;
            }

            entry.setValue(str.substring(0, 50) + "..." + str.substring(len - 50));
        }

        return message;
    }

    public static Boolean isJsonObject(String json) {
        return null != json && !json.trim().isEmpty() && json.trim().startsWith("{");
    }

    /**
     * @see #toJSONString(Object)
     */
    public static Object toJSONStringSimply(Object val) {
        if (null == val) {
            return null;
        }

        ValueFilter valueLenFixFilter = new ValueFilter() {
            public Object process(Object object, String name, Object value) {
                if (null != value && value instanceof String) {
                    return StringUtil.cutLeft(value + "", 100, "...");
                }

                return value;
            }
        };

        SerializeFilter[] serializeFilters = new SerializeFilter[] { valueLenFixFilter };

        try {
            return JSON.toJSONString(val, SerializeConfig.globalInstance, serializeFilters, DATE_FORMAT, JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.DisableCircularReferenceDetect);
        } catch (Throwable e) {
            log.error("#18 type=" + val.getClass().getName() + ", e=" + e);

            return JSON.toJSONString(val, SerializeConfig.globalInstance, serializeFilters, DATE_FORMAT, JSON.DEFAULT_GENERATE_FEATURE);
        }
    }

    public static JSONArray toJSONArray(Object value) {
        return (JSONArray) JSON.toJSON(value);
    }

    public static <T> List<T> getList(JSONObject jsonObject, String key, Class<T> type) {
        if (null == jsonObject || null == key || null == type || !jsonObject.containsKey(key)) {
            return null;
        }

        JSONArray jsonArray = jsonObject.getJSONArray(key);
        return jsonArray.stream().map(e -> ((JSONObject) e).toJavaObject(type)).collect(Collectors.toList());
    }

    public static String getString(JSONObject jsonObject, String key) {
        if (null == jsonObject || null == key) {
            return null;
        }

        return jsonObject.getString(key);
    }

    public static String getString(String json, String key) {
        JSONObject jsonObject = parseObject(json);
        return getString(jsonObject, key);
    }

    public static Integer getInteger(String json, String key) {
        String value = getString(json, key);
        return NumberUtil.toInteger(value);
    }

    public static Integer getInteger(JSONObject jsonObject, String key) {
        String value = getString(jsonObject, key);
        return NumberUtil.toInteger(value);
    }
}